/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.inspur.edp.lcm.metadata.core.persistence;

import com.inspur.edp.lcm.metadata.api.entity.OperationEnum;
import com.inspur.edp.lcm.metadata.common.FileServiceImp;
import com.inspur.edp.lcm.metadata.common.Utils;
import com.inspur.edp.lcm.metadata.common.util.PropertiesUtil;
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

public class SourceDataRepository {
    /**
     * 影响此操作的元数据是否变化
     *
     * @param projPath      工程路径
     * @param operationEnum 操作
     * @param metadataPaths 影响操作的元数据
     * @return 元数据是否变化
     */
    public boolean isMetadataChanged(String projPath, OperationEnum operationEnum, List<String> metadataPaths) {
        String recordFilePath = FileServiceImp.combinePath(projPath, Utils.getMetadataProjPath(), Utils.getMetadataBinPath(), "metadata_" + operationEnum.toString());
        if (!new File(recordFilePath).exists()) {
            return true;
        }
        Properties properties = PropertiesUtil.getProperties(recordFilePath);

        // 判断删除的文件
        if (properties.values().size() != metadataPaths.size()) {
            return true;
        }

        for (String metadataPath : metadataPaths) {
            String timeString = properties.getProperty(FileServiceImp.getRelativePath(metadataPath, projPath));
            if (StringUtils.isEmpty(timeString) || !StringUtils.isNumeric(timeString) || new File(metadataPath).lastModified() != Long.parseLong(timeString)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 更新元数据变化记录
     *
     * @param projPath      工程路径
     * @param operationEnum 操作
     * @param metadataPaths 影响的元数据
     */
    public void updateMetadataChanges(String projPath, OperationEnum operationEnum, List<String> metadataPaths) {
        String recordFilePath = FileServiceImp.combinePath(projPath, Utils.getMetadataProjPath(), Utils.getMetadataBinPath(), "metadata_" + operationEnum.toString());
        FileServiceImp.createDirAndFile(recordFilePath);
        Properties properties = new Properties();
        metadataPaths.forEach(metadataPath -> properties.setProperty(FileServiceImp.getRelativePath(metadataPath, projPath), String.valueOf(new File(metadataPath).lastModified())));
        PropertiesUtil.setProperties(recordFilePath, properties);
    }

    /**
     * 影响此操作的文件是否变化
     *
     * @param projPath      工程路径
     * @param absolutePath  指定的文件或者文件夹
     * @param operationEnum 操作
     * @return 文件是否变化
     */
    public boolean isFileChanged(String projPath, String absolutePath, OperationEnum operationEnum, FilenameFilter filenameFilter) {
        String recordFilePath = FileServiceImp.combinePath(projPath, Utils.getMetadataProjPath(), Utils.getMetadataBinPath(), operationEnum.toString());
        if (!new File(recordFilePath).exists()) {
            return true;
        }
        Properties properties = PropertiesUtil.getProperties(recordFilePath);
        File targetFile = new File(absolutePath);
        String targetRelativePath = FileServiceImp.getRelativePath(absolutePath, projPath);
        if (targetFile.isFile()) {
            String timeString = properties.getProperty(targetRelativePath);
            return StringUtils.isEmpty(timeString) || !StringUtils.isNumeric(timeString) || targetFile.lastModified() != Long.parseLong(timeString);
        }
        // 过滤掉隐藏的文件
        List<File> targetFiles = FileServiceImp.listAllFilesFilterDirAndFile(targetFile, filenameFilter);

        // 判断删除的文件
        List<String> keys = properties.stringPropertyNames().stream().filter(key -> key.startsWith(targetRelativePath)).collect(Collectors.toList());
        if (keys.size() != targetFiles.size()) {
            return true;
        }

        for (File sourceFile : targetFiles) {
            String timeString = properties.getProperty(FileServiceImp.getRelativePath(sourceFile.getPath(), projPath));
            if (StringUtils.isEmpty(timeString) || !StringUtils.isNumeric(timeString) || sourceFile.lastModified() != Long.parseLong(timeString)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 影响此操作的文件是否变化
     *
     * @param projPath      工程路径
     * @param key           键
     * @param value         值
     * @param operationEnum 操作
     * @return 文件是否变化
     */
    public boolean isFileChanged(String projPath, String key, String value, OperationEnum operationEnum) {
        Utils.checkNPE(value, "value不可为空");
        String recordFilePath = FileServiceImp.combinePath(projPath, Utils.getMetadataProjPath(), Utils.getMetadataBinPath(), operationEnum.toString());
        if (!new File(recordFilePath).exists()) {
            return true;
        }
        Properties properties = PropertiesUtil.getProperties(recordFilePath);
        return !value.equals(properties.getProperty(key));
    }

    /**
     * 更新文件变化记录
     *
     * @param projPath      工程路径
     * @param absolutePath  指定的文件或者文件夹
     * @param operationEnum 操作
     */
    public void updateFileChanges(String projPath, String absolutePath, OperationEnum operationEnum, FilenameFilter filenameFilter) {
        String recordFilePath = FileServiceImp.combinePath(projPath, Utils.getMetadataProjPath(), Utils.getMetadataBinPath(), operationEnum.toString());
        FileServiceImp.createDirAndFile(recordFilePath);
        Properties properties = PropertiesUtil.getProperties(recordFilePath);
        File targetFile = new File(absolutePath);
        String targetRelativePath = FileServiceImp.getRelativePath(absolutePath, projPath);
        if (targetFile.isFile()) {
            properties.setProperty(targetRelativePath, String.valueOf(targetFile.lastModified()));
        } else {
            List<File> targetFiles = FileServiceImp.listAllFilesFilterDirAndFile(targetFile, filenameFilter);
            // 获取删除的文件
            List<String> keys = properties.stringPropertyNames().stream().filter(key -> key.startsWith(targetRelativePath)).collect(Collectors.toList());
            for (File sourceFile : targetFiles) {
                String key = FileServiceImp.getRelativePath(sourceFile.getPath(), projPath);
                properties.setProperty(key, String.valueOf(sourceFile.lastModified()));
                keys.remove(key);
            }
            if (!CollectionUtils.isEmpty(keys)) {
                for (String key : keys) {
                    properties.remove(key);
                }
            }
        }
        PropertiesUtil.setProperties(recordFilePath, properties);
    }

    /**
     * 更新文件变化记录
     *
     * @param projPath      工程路径
     * @param key           键
     * @param value         值
     * @param operationEnum 操作
     */
    public void updateFileChanges(String projPath, String key, String value, OperationEnum operationEnum) {
        String recordFilePath = FileServiceImp.combinePath(projPath, Utils.getMetadataProjPath(), Utils.getMetadataBinPath(), operationEnum.toString());
        FileServiceImp.createDirAndFile(recordFilePath);
        Properties properties = PropertiesUtil.getProperties(recordFilePath);
        properties.setProperty(key, value);
        PropertiesUtil.setProperties(recordFilePath, properties);
    }

    /**
     * 判断文件内容是否变化
     *
     * @param projPath      工程路径
     * @param key           指定的文件标识
     * @param contentStr    文件的内容字符串
     * @param operationEnum 操作
     * @return 文件内容是否变化
     */
    public boolean isContentChanged(String projPath, String key, String contentStr, OperationEnum operationEnum) {
        String recordFilePath = FileServiceImp.combinePath(projPath, Utils.getMetadataProjPath(), Utils.getMetadataBinPath(), operationEnum.toString());
        if (!new File(recordFilePath).exists()) {
            return true;
        }
        Properties properties = PropertiesUtil.getProperties(recordFilePath);
        String contentSha1 = DigestUtils.sha1Hex(contentStr);
        String timeString = properties.getProperty(key);
        return StringUtils.isEmpty(timeString) || !contentSha1.equals(properties.getProperty(key));
    }

    /**
     * 更新内容变化记录
     *
     * @param projPath      工程路径
     * @param key           变化的文件标识
     * @param contentStr    内容字符串
     * @param operationEnum 操作
     */
    public void updateContentChanges(String projPath, String key, String contentStr, OperationEnum operationEnum) {
        String recordFilePath = FileServiceImp.combinePath(projPath, Utils.getMetadataProjPath(), Utils.getMetadataBinPath(), operationEnum.toString());
        FileServiceImp.createDirAndFile(recordFilePath);
        Properties properties = PropertiesUtil.getProperties(recordFilePath);
        properties.setProperty(key, DigestUtils.sha1Hex(contentStr));
        PropertiesUtil.setProperties(recordFilePath, properties);
    }

    /**
     * 获取变动的文件列表
     *
     * @param projPath      工程路径
     * @param absolutePath  指定的文件或者文件夹
     * @param operationEnum 操作
     * @return 变动的文件列表
     */
    public List<File> getChangeFiles(String projPath, String absolutePath, OperationEnum operationEnum) {
        String recordFilePath = FileServiceImp.combinePath(projPath, Utils.getMetadataProjPath(), Utils.getMetadataBinPath(), operationEnum.toString());
        File targetFile = new File(absolutePath);
        if (!new File(recordFilePath).exists()) {
            return targetFile.isFile() ? Collections.singletonList(targetFile) : FileServiceImp.listAllFilesFilterDirAndFile(targetFile, null);
        }

        List<File> changeFiles = new ArrayList<>();
        Properties properties = PropertiesUtil.getProperties(recordFilePath);
        if (targetFile.isFile()) {
            String timeString = properties.getProperty(FileServiceImp.getRelativePath(targetFile.getPath(), projPath));
            if (StringUtils.isEmpty(timeString) || !StringUtils.isNumeric(timeString) || targetFile.lastModified() != Long.parseLong(timeString)) {
                changeFiles.add(targetFile);
            }
            return changeFiles;
        }
        // 不处理删除的
        List<File> targetFiles = FileServiceImp.listAllFilesFilterDirAndFile(targetFile, null);
        for (File sourceFile : targetFiles) {
            String timeString = properties.getProperty(FileServiceImp.getRelativePath(sourceFile.getPath(), projPath));
            if (StringUtils.isEmpty(timeString) || !StringUtils.isNumeric(timeString) || sourceFile.lastModified() != Long.parseLong(timeString)) {
                changeFiles.add(sourceFile);
            }
        }
        return changeFiles;
    }

    public void deleteChangeRecord(String projPath, OperationEnum operationEnum) {
        File metadataRecordFile = new File(FileServiceImp.combinePath(projPath, Utils.getMetadataProjPath(), Utils.getMetadataBinPath(), "metadata_" + operationEnum.toString()));
        if (metadataRecordFile.exists()) {
            metadataRecordFile.delete();
        }
        File recordFile = new File(FileServiceImp.combinePath(projPath, Utils.getMetadataProjPath(), Utils.getMetadataBinPath(), operationEnum.toString()));
        if (recordFile.exists()) {
            recordFile.delete();
        }
    }
}
